<?php

Raise::load('core.geometry.RaiseShape');
Raise::load('core.geometry.RaisePoint');
Raise::load('core.reflection.RaiseVariable');
Raise::load('core.collection.IRaiseCollectionConvertible');

/**
 * Rectangle class
 * to define a rectangle
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package phpraise.core.geometry
 * @since 1.2
 */
class RaiseRectangle extends RaiseShape implements IRaiseCollectionConvertible {

    /**
     * Location of the top left corner
     * @var RaisePoint
     */
    private $location;

    /**
     * Width of the rectangle
     * @var integer|double
     */
    private $width;

    /**
     * Height of the rectangle
     * @var integer|double
     */
    private $height;

    /**
     * Creates a RaiseRectangle object
     * @param RaisePoint $l Initial location to set
     * @param integer|double $w Initial width to set
     * @param integer|double $h Initial height to set
     */
    function __construct($l, $w, $h) {
        $args = func_get_args();
        if (count($args) == 4) {
            $this->location = new RaisePoint($args[0], $args[1]);
            $this->width = $args[2];
            $this->height = $args[3];
        } elseif (count($args) == 3 && $args[0] instanceof RaisePoint) {
            $this->location = $args[0];
            $this->width = $args[1];
            $this->height = $args[2];
        }
        if($this->width < 0 || $this->height < 0){
            throw new RaiseInvalidArgumentException("Width and height of a rectangle must be 0 or positive integer, i.e. x >= 0");
        }
    }

    /**
     * Get or set the width of the rectangle
     * @param integer|double $w (Optional) When specified, the width of the rectangle will be set to this value
     * @return integer|double The width of the rectangle
     */
    public function width($w = false){
        if($w !== false){
            if($w < 0){
                throw new RaiseInvalidArgumentException("Width of a rectangle must be 0 or positive integer, i.e. x >= 0");
            }
            $this->width = $w;
        }
        return $this->width;
    }

    /**
     * Get or set the height of the rectangle
     * @param integer|double $h (Optional) When specified, the height of the rectangle will be set to this value
     * @return integer|double The height of the rectangle
     */
    public function height($h = false){
        if($h !== false){
            if($h < 0){
                throw new RaiseInvalidArgumentException("Height of a rectangle must be 0 or positive integer, i.e. x >= 0");
            }
            $this->height = $h;
        }
        return $this->height;
    }

    /**
     * Get or set the X coordinate of the top left corner of the rectangle
     * @param integer|double $x (Optional) When specified, the X-coordinate of the rectangle will be set to this value
     * @return integer|double
     */
    public function x($x = false){
        if($x !== false){
            $this->location->x($x);
        }
        return $this->location->x();
    }

    /**
     * Get or set the Y coordinate of the top left corner of the rectangle
     * @param integer|double $y (Optional) When specified, the Y-coordinate of the rectangle will be set to this value
     * @return integer|double
     */
    public function y($y = false){
        if($y !== false){
            $this->location->y($y);
        }
        return $this->location->y();
    }

    /**
     * Get or set the location of the rectangle
     * @param RaisePoint $l (Optional) When specified, the top left coordinate of the rectangle will be set to this value
     * @return RaisePoint The current location of the rectangle
     */
    public function location($l = false){
        if($l !== false){
            if(RaiseVariable::isNotA($l, 'RaisePoint')){
                throw new RaiseInvalidArgumentException("Location has to be a RaisePoint instance");
            }
            $this->location = $l;
        }
        return $this->location;
    }

    /**
     * Get the X coordinate of the bottom right corner of the rectangle
     * Determined by the X-coordinate + width of the rectangle
     * @return integer|double
     */
    public function x2(){
        return $this->location->x() + $this->width();
    }

    /**
     * Get the Y coordinate of the bottom right corner of the rectangle
     * Determined by the Y-coordinate + height of the rectangle
     * @return integer|double
     */
    public function y2(){
        return $this->location->y() + $this->height();
    }

    /**
     * Get the center RaisePoint of the rectangle
     * @return RaisePoint
     */
    public function center(){
        return new RaisePoint($this->centerX(), $this->centerY());
    }

    /**
     * Get the X coordinate of the center of the rectangle
     * @return integer|double
     */
    public function centerX(){
        return $this->location->x() + $this->width() / 2.0;
    }

    /**
     * Get the Y coordinate of the center of the rectangle
     * @return integer|double
     */
    public function centerY(){
        return $this->location->y() + $this->height() / 2.0;
    }

    /**
     * Get the diagonal length / hypotenuse of the rectangle
     * @return integer|double
     */
    public function diagonal(){
        return hypot($this->width, $this->height);
    }

    /**
     * Get the rectangle in the form of an 1D collection (x, y, w, h)
     * @return RaiseCollection
     */
    public function toCollection() {
        return new RaiseCollection($this->location->x(), $this->location->y(), $this->width(), $this->height());
    }
    
    /**
     * Get the rectangle in the form of a string
     * @return string
     */
    function __toString(){
        return $this->location->x() . ',' . $this->location->y() . ',' . $this->width() . ',' . $this->height();
    }

    /**
     * Compare whether both Rectangle values are equal or not
     * @param RaiseRectangle $o
     * @return boolean
     */
    public function equals($o) {
        return $this->x() == $o->x() && $this->y() == $o->y() && $this->width() == $o->width() && $this->height() == $o->height();
    }

}

